﻿using System.Linq;
using System.Reflection;

namespace ALinq.Mapping
{
    /// <summary>
    /// Represents an association relationship between two entity types.
    /// </summary>
    public class AssociationMapping : ModelMapping
    {
        private MemberInfo member;
        private MemberInfo[] thisMember;
        private MemberInfo[] otherMember;

        /// <summary>
        /// Initializes a new instance of the ALinq.Mapping.AssociationMapping class.
        /// </summary>
        /// <param name="member">The underlying System.Reflection.MemberInfo.</param>
        public AssociationMapping(MemberInfo member)
            : base(ModelMappingType.Association)
        {
            this.Member = member;
        }

        /// <summary>
        /// Gets the name of the constraint in the database.
        /// </summary>
        public string Name
        {
            get { return GetAttribute(() => Name); }
            set { SetAttribute(() => Name, value); }
        }

        /// <summary>
        /// Gets the underlying System.Reflection.MemberInfo.
        /// </summary>
        public MemberInfo Member
        {
            get { return this.member; }
            private set
            {
                if (value == null)
                    throw Error.MemberValueCanNotNull("Member");

                this.member = value;
                SetAttribute("Member", value.Name);
            }
        }

        /// <summary>
        /// Gets name of the member that stores the data for this member.
        /// </summary>
        public string Storage
        {
            get { return GetAttribute(() => Storage); }
            set { SetAttribute(() => Storage, value); }
        }

        /// <summary>
        /// Gets the member on this side that represents the association.
        /// </summary>
        public MemberInfo[] ThisMember
        {
            get { return this.thisMember; }
            set
            {
                if (value == null)
                    throw Error.MemberValueCanNotNull(() => ThisMember);

                //===========================================
                // 验证所有 member 为同一个类的。
                var type = value[0].DeclaringType;
                for (var i = 1; i < value.Length; i++)
                {
                    if(type != value[i].DeclaringType)
                    {
                        throw Error.InvalidMember(value[i], type);
                    }
                }
                //===========================================

                thisMember = value;
                var keys = string.Join(",", value.Select(o => o.Name).ToArray());
                ThisKey = keys;
            }
        }

        /// <summary>
        /// Gets the member on the other side of this association that represents 
        /// </summary>
        public MemberInfo[] OtherMember
        {
            get { return this.otherMember; }
            set
            {
                if (value == null)
                    throw Error.MemberValueCanNotNull(() => OtherMember);

                //===========================================
                // 验证所有 member 为同一个类的。
                var type = value[0].DeclaringType;
                for (var i = 1; i < value.Length; i++)
                {
                    if (type != value[i].DeclaringType)
                    {
                        throw Error.InvalidMember(value[i], type);
                    }
                }
                //===========================================

                otherMember = value;
                var keys = string.Join(",", value.Select(o => o.Name).ToArray());
                OtherKey = keys;
            }
        }

        /// <summary>
        /// Gets a string representing the values on this side of the association.
        /// </summary>
        public string ThisKey
        {
            get { return this.GetAttribute(() => ThisKey); }
            private set { this.SetAttribute(() => ThisKey, value); }
        }

        /// <summary>
        /// Gets a string that represents the values on the other side of the association.
        /// </summary>
        public string OtherKey
        {
            get { return this.GetAttribute(() => OtherKey); }
            private set { this.SetAttribute(() => OtherKey, value); }
        }

        /// <summary>
        /// Gets whether the other type is the parent of this type.
        /// </summary>
        public bool IsForeignKey
        {
            get { return this.GetAttribute(() => IsForeignKey); }
            set { this.SetAttribute(() => IsForeignKey, value); }
        }
    }
}